package com.bestcem.xm.ticket.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.domain.web.XmResultJson;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.uitls.StringUtil;
import com.bestcem.xm.ticket.constant.CommonConstant;
import com.bestcem.xm.ticket.constant.Constants;
import com.bestcem.xm.ticket.controller.param.ticket.template.TemplateParam;
import com.bestcem.xm.ticket.controller.param.ticket.template.UpdateTemplateParam;
import com.bestcem.xm.ticket.enums.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 校验提交的模板是否合法
 *
 * @author xuan.yang <xuan.yang@idiaoyan.com>
 * @version v2.3
 * @date 2021/9/2 14:26
 */
@Slf4j
public class TemplateCheckUtil {

    /**
     * 校验保存的实体类是否合法
     * 如果返回null代表正确，非空返回错误信息
     *
     * @param templateParam
     * @return
     */
    public static ServiceResult<String> checkSaveTemplate(TemplateParam templateParam) {
        if (ObjectUtil.isEmpty(templateParam) || ObjectUtil.isEmpty(TemplateTypeEnum.getByIndex(templateParam.getType()))) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "模板不能为空");
        }
        if (StrUtil.hasBlank(templateParam.getName())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "模板名称不能为空");
        }
        List<TemplateParam.FieldInfo> fieldList = templateParam.getFieldList();
        if (CollectionUtil.isEmpty(fieldList)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "模板字段列表不能为空");
        }
        //校验数组里面是否有不合法的数据，只要存在不合法数据就判断为false
        Set<String> hashSet = new HashSet<>();
        for (TemplateParam.FieldInfo fieldInfo : fieldList) {
            if (StringUtil.isNullOrEmpty(fieldInfo.getName())) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "字段名称不能为空");
            }
            if (fieldInfo.getName().length() > CommonConstant.MAX_CHARACTER_LENGTH_TEN) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "字段名称长度不能大于10");
            }
            if (!hashSet.add(fieldInfo.getName())) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "字段名称重复");
            }
            TemplateFieldTypeEnum typeEnum = TemplateFieldTypeEnum.getByIndex(fieldInfo.getType());
            if (Objects.isNull(typeEnum)) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_RANGE, "字段类型为空或超出合法范围");
            }
            // 不能为空但长度不做限制
            if (StringUtil.isNullOrEmpty(fieldInfo.getField())) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "字段key field不能为空");
            }
            ServiceResult<String> checkResult = checkEmumIsIegal(typeEnum, fieldInfo);
            if (!checkResult.isSuccess() && !checkResult.getErrorCode().equals("0")) {
                log.warn("参数不合法{}", checkResult);
                return checkResult;
            }
        }

        return ServiceResult.success();
    }

    /**
     * 检查各种枚举值对应传来的字段是否合理
     *
     * @param typeEnum
     * @param fieldInfo
     * @return
     */
    private static ServiceResult<String> checkEmumIsIegal(TemplateFieldTypeEnum typeEnum, TemplateParam.FieldInfo fieldInfo) {
        switch (typeEnum) {
            case TEXT:
            case MULTI_LINE:
                return ServiceResult.success();
            case SELECT:
                TemplateParam.SingleSelect singleSelect = fieldInfo.getSingleSelect();
                // 传来的枚举字段是否为合理
                if (ObjectUtil.isEmpty(singleSelect) || ObjectUtil.isEmpty(TemplateSelectSourceEnum.getByIndex(singleSelect.getSource()))) {
                    return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "单选选择列表不能为空或参数超出范围");
                }

                if (TemplateSelectSourceEnum.CUSTOMER_SOURCE.getIndex().equals(singleSelect.getSource())) {
                    //自定义数据需要检测列表是否存在
                    if (CollUtil.isEmpty(singleSelect.getOptionList())) {
                        return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "选项列表options不能为空");
                    }
                    if (singleSelect.getOptionList().size() > CommonConstant.MAX_LIST_SIZE_TEN) {
                        return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项列表options最多10条");
                    }
                    HashSet<String> optionHashSet = new HashSet<>();
                    for (TemplateParam.SingleSelect.Option option : singleSelect.getOptionList()) {
                        if (Objects.isNull(option.getValue())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "选项内容对应的前端标识value不能为空");
                        }
                        if (option.getValue() >= CommonConstant.MAX_LIST_SIZE_TEN) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项内容对应的前端标识value不能超过10");
                        }
                        if (StrUtil.isBlank(option.getField())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "选项内容不能为空");
                        }
                        if (option.getField().length() > CommonConstant.MAX_CHARACTER_LENGTH_TWENTY) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项内容长度不能大于20");
                        }
                        if (!optionHashSet.add(option.getField())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项内容不可重名");
                        }

                    }

                }
                break;

            case CASCADE_SELECT:
                List<TemplateParam.Cascade> cascadeList = fieldInfo.getCascadeList();
                // 判断穿进来的层级是否和列表中层级匹配
                if (CollUtil.isEmpty(cascadeList)) {
                    return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "级联数据数组cascade不能为空");
                }
                XmResultJson checkCascadeResult = checkCascadeLength(cascadeList);
                if (!checkCascadeResult.getCode().equals(0)) {
                    return ServiceResult.fail(String.valueOf(checkCascadeResult.getCode()), checkCascadeResult.getMsg());
                }
                //判断每个层级是否进行匹配
                for (TemplateParam.Cascade cascade : cascadeList) {
                    Integer level = cascade.getLevel();
                    HashSet<String> currLevelSet = new HashSet<>();
                    if (Objects.isNull(level)) {
                        return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项层级level不能为空");
                    }
                    TicketTemplateCascadeLevelEnum levelEnum = TicketTemplateCascadeLevelEnum.getByIndex(level);
                    if (Objects.isNull(levelEnum)) {
                        return ServiceResult.fail(ReqErrorCodeEnum.PARAM_RANGE, "级联选项层级level超出合法范围");
                    }
                    List<TemplateParam.Option> optionList = cascade.getOptionList();

                    if (CollectionUtils.isEmpty(optionList)) {
                        return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "选项列表options不能为空");
                    }

                    for (TemplateParam.Option option : optionList) {
                        if (Objects.isNull(option.getId())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项id不能为空");
                        }
                        if (Objects.isNull(option.getPid())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项pid不能为空");
                        }
                        if (Objects.isNull(option.getValue())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项节点名称不能为空");
                        }
                        if (option.getValue().length() > CommonConstant.MAX_CHARACTER_LENGTH_TWENTY) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "级联选项节点名称长度不能超过20");
                        }
                        if (!currLevelSet.add(option.getValue())) {
                            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "级联选项节点名称不可重复");
                        }
                    }
                }
                break;
            default:
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "字段类型不匹配");

        }
        return ServiceResult.success();
    }

    /**
     * 校验UpdateTemplateParam参数以及orgId,userId,id
     *
     * @param updateTemplateParam 入参
     * @param orgId               公司id
     * @param userId              用户id
     * @param id                  模板id
     * @return 校验成功与否
     * @author ympan <yuming.pan@idiaoyan.com>
     * @date 23:08 2021/9/9
     */
    public static XmResultJson<String> checkUpdateTemplateParam(UpdateTemplateParam updateTemplateParam, String orgId, String userId, String id) {
        // 针对名称修改/全部修改 共通内容的校验
        if (!ObjectId.isValid(orgId)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "公司id格式错误");
        }
        if (!ObjectId.isValid(userId)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "用户id格式错误");
        }
        if (!ObjectId.isValid(id)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "模板id格式错误");
        }
        if (Objects.isNull(updateTemplateParam)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "updateTemplateParam不能为空");
        }
        if (StrUtil.hasBlank(updateTemplateParam.getName())) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "模板名称不能为空");
        }
        Integer flag = updateTemplateParam.getFlag();
        if (Objects.isNull(flag)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "flag不能为空");
        }
        UpdateTemplateEnum updateTemplateEnum = UpdateTemplateEnum.getByIndex(flag);
        if (Objects.isNull(updateTemplateEnum)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_RANGE, "flag参数超出合法范围");
        }
        // 如果只修改名称，直接返回即可
        if (flag.equals(UpdateTemplateEnum.UPDATE_NAME_ONLY.getIndex())) {
            return XmResultJson.success();
        }
        List<UpdateTemplateParam.FieldInfo> fieldList = updateTemplateParam.getFieldList();
        // 模板字段列表不能为空，但长度不限
        if (CollUtil.isEmpty(fieldList)) {
            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "模板字段列表不能为空");
        }
        Set<String> hashSet = new HashSet<>();
        for (UpdateTemplateParam.FieldInfo fieldInfo : fieldList) {
            if (StringUtil.isNullOrEmpty(fieldInfo.getName())) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "字段名称不能为空");
            }
            if (fieldInfo.getName().length() > CommonConstant.MAX_CHARACTER_LENGTH_TEN) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "字段名称长度不能大于10");
            }
            if (!hashSet.add(fieldInfo.getName())) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "字段名称重复");
            }
            TemplateFieldTypeEnum typeEnum = TemplateFieldTypeEnum.getByIndex(fieldInfo.getType());
            if (Objects.isNull(typeEnum)) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_RANGE, "字段类型为空或超出合法范围");
            }
            // 不能为空但长度不做限制
            if (StringUtil.isNullOrEmpty(fieldInfo.getField())) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "字段key field不能为空");
            }
            // required 默认为false
            if (Objects.isNull(fieldInfo.getRequired())) {
                fieldInfo.setRequired(false);
            }
            // 校验fieldInfo参数
            switch (typeEnum) {
                case TEXT:
                case MULTI_LINE:
                    // TEXT和MULTI_LINE的场合，无需做校验
                    break;
                case SELECT:
                    UpdateTemplateParam.SingleSelect singleSelect = fieldInfo.getSingleSelect();
                    if (Objects.isNull(singleSelect)) {
                        return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "单选选择列表singleSelect不能为空");
                    }
                    if (Objects.isNull(TemplateSelectSourceEnum.getByIndex(singleSelect.getSource()))) {
                        return XmResultJson.fail(ReqErrorCodeEnum.PARAM_RANGE, "数据来源超出合法范围");
                    }
                    // 选项来源为手动添加的时候，需要校验选项列表
                    if (TemplateSelectSourceEnum.CUSTOMER_SOURCE.getIndex().equals(singleSelect.getSource())) {
                        if (CollUtil.isEmpty(singleSelect.getOptionList())) {
                            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "选项列表options不能为空");
                        }
                        if (singleSelect.getOptionList().size() > CommonConstant.MAX_LIST_SIZE_TEN) {
                            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项列表options最多10条");
                        }
                        HashSet<String> optionHashSet = new HashSet<>();
                        for (UpdateTemplateParam.SingleSelect.Option option : singleSelect.getOptionList()) {
                            if (Objects.isNull(option.getValue())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "选项内容对应的前端标识value不能为空");
                            }
                            if (option.getValue() >= CommonConstant.MAX_LIST_SIZE_TEN) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项内容对应的前端标识value不能超过10");
                            }
                            if (StrUtil.isBlank(option.getField())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "选项内容不能为空");
                            }
                            if (option.getField().length() > CommonConstant.MAX_CHARACTER_LENGTH_TWENTY) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项内容长度不能大于20");
                            }
                            if (!optionHashSet.add(option.getField())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "选项内容不可重名");
                            }
                        }
                    }
                    break;
                case CASCADE_SELECT:
                    List<UpdateTemplateParam.Cascade> cascadeList = fieldInfo.getCascadeList();
                    if (CollUtil.isEmpty(cascadeList)) {
                        return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "级联数据数组cascade不能为空");
                    }
                    for (UpdateTemplateParam.Cascade cascade : cascadeList) {
                        if (Objects.isNull(cascade)) {
                            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "单一层级选项列表cascade不能为空");
                        }
                        Integer level = cascade.getLevel();
                        HashSet<String> currLevelSet = new HashSet<>();
                        if (Objects.isNull(level)) {
                            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项层级level不能为空");
                        }
                        TicketTemplateCascadeLevelEnum levelEnum = TicketTemplateCascadeLevelEnum.getByIndex(level);
                        if (Objects.isNull(levelEnum)) {
                            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_RANGE, "级联选项层级level超出合法范围");
                        }
                        List<UpdateTemplateParam.Cascade.Option> optionList = cascade.getOptionList();
                        if (CollectionUtils.isEmpty(optionList)) {
                            return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "选项列表options不能为空");
                        }
                        for (UpdateTemplateParam.Cascade.Option option : optionList) {
                            if (Objects.isNull(option)) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项option不能为空");
                            }
                            if (Objects.isNull(option.getId())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项id不能为空");
                            }
                            if (Objects.isNull(option.getPid())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项pid不能为空");
                            }
                            if (Objects.isNull(option.getValue())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "级联选项节点名称不能为空");
                            }
                            if (option.getValue().length() > CommonConstant.MAX_CHARACTER_LENGTH_TWENTY) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "级联选项节点名称长度不能超过20");
                            }
                            if (!currLevelSet.add(option.getValue())) {
                                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "级联选项节点名称不可重复");
                            }
                        }
                    }
                    // 校验每单一级选项不超过300项
                    XmResultJson<String> checkCascadeResult = checkCascadeLengthForUpd(cascadeList);
                    if (!checkCascadeResult.getCode().equals(0)) {
                        return XmResultJson.fail(checkCascadeResult.getCode(), checkCascadeResult.getMsg());
                    }
                    break;
                default:
                    return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, "字段类型不匹配");
            }
        }
        return XmResultJson.success();
    }

    /**
     * 检测每级的长度是否符合
     * 先按照pid进行分组，检测分组后的长度是否符合
     * 校验通过返回success，失败提示信息msg
     *
     * @param cascadeList
     * @return
     */
    private static XmResultJson checkCascadeLength(List<TemplateParam.Cascade> cascadeList) {
        if (CollectionUtil.isEmpty(cascadeList)) {
            if (log.isInfoEnabled()) {
                log.info("------分级列表为空-----");
            }
            return XmResultJson.success();
        }
        for (int i = 0; i < cascadeList.size(); i++) {
            // 如果第一级的话判断长度，否则后面是根据pid进行操作
            TemplateParam.Cascade cascade = cascadeList.get(i);
            if (Objects.isNull(cascade)) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "单一层级选项列表cascade不能为空");
            }
            List<TemplateParam.Option> options = cascade.getOptionList();
            if (i == 0 && CollectionUtil.isNotEmpty(options)) {
                if (options.size() > Constants.CASCADE_MAX_HEIGHT) {
                    log.error(Constants.CASCADE_MAX_HEIGHT_MSG);
                    return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, Constants.CASCADE_MAX_HEIGHT_MSG);
                }
            } else if (CollectionUtil.isNotEmpty(cascade.getOptionList())) {
                Map<String, List<TemplateParam.Option>> optionMap = options.stream().collect(Collectors.groupingBy(option -> option.getPid()));

                for (Map.Entry<String, List<TemplateParam.Option>> entry : optionMap.entrySet()) {
                    // 判断value的长度是否超过300
                    if (entry.getValue().size() > Constants.CASCADE_MAX_HEIGHT) {
                        log.error(Constants.CASCADE_MAX_HEIGHT_MSG);
                        return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, Constants.CASCADE_MAX_HEIGHT_MSG);
                    }
                }
            }
        }
        // 循环过后没有返回说明校验成功
        return XmResultJson.success();
    }

    /**
     * 检测每级的长度是否符合
     * 先按照pid进行分组，检测分组后的长度是否符合
     * 校验通过返回success，失败提示信息msg
     *
     * @param cascadeList 级联选项列表
     * @return 校验成功与否
     */
    private static XmResultJson<String> checkCascadeLengthForUpd(List<UpdateTemplateParam.Cascade> cascadeList) {
        for (int i = 0; i < cascadeList.size(); i++) {
            // 如果第一级的话判断长度，否则后面是根据pid进行操作
            UpdateTemplateParam.Cascade cascade = cascadeList.get(i);
            if (Objects.isNull(cascade)) {
                return XmResultJson.fail(ReqErrorCodeEnum.PARAM_LOST, "单一层级选项列表cascade不能为空");
            }
            List<UpdateTemplateParam.Cascade.Option> options = cascade.getOptionList();
            if (i == 0 && CollUtil.isNotEmpty(options)) {
                if (options.size() > Constants.CASCADE_MAX_HEIGHT) {
                    log.error(Constants.CASCADE_MAX_HEIGHT_MSG);
                    return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, Constants.CASCADE_MAX_HEIGHT_MSG);
                }
            } else if (CollUtil.isNotEmpty(cascade.getOptionList())) {
                Map<String, List<UpdateTemplateParam.Cascade.Option>> optionMap = options.stream().collect(Collectors.groupingBy(UpdateTemplateParam.Cascade.Option::getPid));
                for (Map.Entry<String, List<UpdateTemplateParam.Cascade.Option>> entry : optionMap.entrySet()) {
                    // 判断value的长度是否超过300
                    if (entry.getValue().size() > Constants.CASCADE_MAX_HEIGHT) {
                        log.error(Constants.CASCADE_MAX_HEIGHT_MSG);
                        return XmResultJson.fail(ReqErrorCodeEnum.PARAM_FORMAT, Constants.CASCADE_MAX_HEIGHT_MSG);
                    }
                }
            }
        }
        // 循环过后没有返回说明校验成功
        return XmResultJson.success();
    }
}
